/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the GLQuake source code.

 The GLQuake source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The GLQuake source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the GLQuake source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// game.cpp - common functions between client and server game modules
//


#include "game.h"


game_t						game;

cvar_t *					com_dedicated;
cvar_t *					com_developer;

cvar_t *					pm_msec;
cvar_t *					pm_jumpHeight;
cvar_t *					pm_walkSpeed;
cvar_t *					pm_runSpeed;
cvar_t *					pm_noclipSpeed;
cvar_t *					pm_spectateSpeed;
cvar_t *					pm_boxWidth;
cvar_t *					pm_spectateBoxSize;
cvar_t *					pm_normalHeight;
cvar_t *					pm_normalViewHeight;
cvar_t *					pm_deadHeight;
cvar_t *					pm_deadViewHeight;
cvar_t *					pm_minNormalPitch;
cvar_t *					pm_maxNormalPitch;

// GLQuake engine framework.
// These will be set by each game module entry point.
glqCommon_t *				glqCommon = NULL;
glqMemory_t *				glqMemory = NULL;
glqFileSystem_t *			glqFileSystem = NULL;
glqCmdSystem_t *			glqCmdSystem = NULL;
glqCVarSystem_t *			glqCVarSystem = NULL;
glqMsgSystem_t *			glqMsgSystem = NULL;
glqDefManager_t *			glqDefManager = NULL;
glqParser_t *				glqParser = NULL;
glqSystem_t *				glqSystem = NULL;
glqTable_t *				glqTable = NULL;
glqCollision_t *			glqCollision = NULL;
glqRenderer_t *				glqRenderer = NULL;
glqSound_t *				glqSound = NULL;
glqGUI_t *					glqGUI = NULL;


/*
 ==================
 G_Printf
 ==================
*/
void G_Printf (const char *fmt, ...){

	char	message[MAX_PRINT_MESSAGE];
	va_list	argPtr;

	va_start(argPtr, fmt);
	Str_VSPrintf(message, sizeof(message), fmt, argPtr);
	va_end(argPtr);

	glqCommon->Printf("%s", message);
}

/*
 ==================
 G_DPrintf
 ==================
*/
void G_DPrintf (const char *fmt, ...){

	char	message[MAX_PRINT_MESSAGE];
	va_list	argPtr;

	if (!com_developer->integerValue)
		return;		// Don't confuse non-developers with techie stuff

	va_start(argPtr, fmt);
	Str_VSPrintf(message, sizeof(message), fmt, argPtr);
	va_end(argPtr);

	glqCommon->Printf("%s", message);
}

/*
 ==================
 G_Error
 ==================
*/
void G_Error (const char *fmt, ...){

	char	message[MAX_PRINT_MESSAGE];
	va_list	argPtr;

	va_start(argPtr, fmt);
	Str_VSPrintf(message, sizeof(message), fmt, argPtr);
	va_end(argPtr);

	glqCommon->Error(false, "%s", message);
}

/*
 ==================
 G_EncodeBounds
 ==================
*/
int G_EncodeBounds (const glqBounds &bounds){

	int		x, y, z;

	if (bounds[0][2] != 0.0f){
		x = glqMath::ClampInt(glqMath::FloatToInt(bounds[1][0]), 1, 1024) - 1;
		y = glqMath::ClampInt(glqMath::FloatToInt(bounds[1][1]), 1, 1024) - 1;
		z = glqMath::ClampInt(glqMath::FloatToInt(bounds[1][2]), 1, 1024) - 1;

		return (x << 0) | (y << 10) | (z << 20) | (1 << 31);
	}

	x = glqMath::ClampInt(glqMath::FloatToInt(bounds[1][0]), 1, 1024) - 1;
	y = glqMath::ClampInt(glqMath::FloatToInt(bounds[1][1]), 1, 1024) - 1;
	z = glqMath::ClampInt(glqMath::FloatToInt(bounds[1][2]), 1, 2048) - 1;

	return (x << 0) | (y << 10) | (z << 20);
}

/*
 ==================

 This is done after each set of userCmd_t on the server, and after local
 prediction on the client
 ==================
*/
void G_PlayerStateToEntityState (playerState_t *ps, entityState_t *es){

	int		i;

	es->number = ps->clientNum;
	es->type = ET_PLAYER;
	es->flags = ps->flags;

	es->weaponType = ps->weaponType;

	// Position and orientation
	es->origin.Init(TR_INTERPOLATE, 0, 0, ps->origin, vec3_origin);
	es->angles.Init(TR_INTERPOLATE, 0, 0, glqAngles(0.0f, ps->viewAngles.yaw, 0.0f), angles_zero);

	// Binding information
	es->bindMaster = ENTITYNUM_NONE;
	es->bindOrientated = false;

	// Event queue
	es->eventSequence = ps->eventSequence;

	for (i = 0; i < MAX_EVENTS; i++){
		es->eventTypes[i] = ps->eventTypes[i];
		es->eventParms[i] = ps->eventParms[i];
	}
}


/*
 ==============================================================================

 INITIALIZATION & SHUTDOWN

 ==============================================================================
*/


/*
 ==================
 
 ==================
*/
void G_Init (bool isClient){

	game.isClient = isClient;

	if (isClient)
		game.memoryTag = TAG_CLIENT;
	else
		game.memoryTag = TAG_SERVER;

	// Set an initial seed for the random number generator
	game.random.SetSeed(glqSystem->Milliseconds());

	// Find engine variables
	com_dedicated = glqCVarSystem->FindVariable("com_dedicated");
	com_developer = glqCVarSystem->FindVariable("com_developer");

	// Register variables
	pm_msec = glqCVarSystem->Register("pm_msec", "8", CVAR_INTEGER, CVAR_NETWORKSYNC | CVAR_GAME, "Time in milliseconds between player movements", 8, GAME_FRAMEMSEC);
	pm_jumpHeight = glqCVarSystem->Register("pm_jumpHeight", "40.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Maximum height the player can jump", 10.0f, 100.0f);	
	pm_walkSpeed = glqCVarSystem->Register("pm_walkSpeed", "100.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Speed the player can move when walking", 10.0f, 1000.0f);
	pm_runSpeed = glqCVarSystem->Register("pm_runSpeed", "150.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Speed the player can move when running", 10.0f, 1000.0f);	
	pm_noclipSpeed = glqCVarSystem->Register("pm_noclipSpeed", "400.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Speed the player can move when noclipping", 10.0f, 1000.0f);
	pm_spectateSpeed = glqCVarSystem->Register("pm_spectateSpeed", "200.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Speed the player can move when spectating", 10.0f, 1000.0f);	
	pm_boxWidth = glqCVarSystem->Register("pm_boxWidth", "32.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Player's bounding box X/Y size", 5.0f, 50.0f);
	pm_spectateBoxSize = glqCVarSystem->Register("pm_spectateBoxSize", "16.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Player's bounding box size when spectating", 5.0f, 50.0f);	
	pm_normalHeight = glqCVarSystem->Register("pm_normalHeight", "74.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Player's bounding box height when standing", 10.0f, 100.0f);
	pm_normalViewHeight = glqCVarSystem->Register("pm_normalViewHeight", "70.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Player's view height when standing", 10.0f, 100.0f);	
	pm_deadHeight = glqCVarSystem->Register("pm_deadHeight", "16.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Player's bounding box height when dead", 10.0f, 100.0f);
	pm_deadViewHeight = glqCVarSystem->Register("pm_deadViewHeight", "12.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Player's view height when dead", 10.0f, 100.0f);	
	pm_minNormalPitch = glqCVarSystem->Register("pm_minNormalPitch", "-85.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Minimum pitch the player can look up when standing", -89.0f, -1.0f);
	pm_maxNormalPitch = glqCVarSystem->Register("pm_maxNormalPitch", "85.0", CVAR_FLOAT, CVAR_NETWORKSYNC | CVAR_GAME, "Maximum pitch the player can look down when standing", 1.0f, 89.0f);

	// Initialize animation system
	Anim_Init();
}

/*
 ==================
 G_Shutdown
 ==================
*/
void G_Shutdown (bool isClient){

	game.isClient = isClient;

	if (isClient)
		game.memoryTag = TAG_CLIENT;
	else
		game.memoryTag = TAG_SERVER;

	// Shutdown animation system
	Anim_Shutdown();

	// Free all game allocations
	glqMemory->FreeAll(game.memoryTag, false);
}